package org.papervision3d.core.proto
{
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;
	
	import org.papervision3d.core.log.PaperLogger;
	import org.papervision3d.objects.DisplayObject3D;
	
	/**
	* <span lang="ja">DisplayObjectContainer3D クラスは、DisplayObject3D コンテナとして動く全てのオブジェクトのための基本クラスです。
	* 各 DisplayObjectContainer3D オブジェクトは、子リストを持ちます。</span>
	* 
	* <span lang="en">The DisplayObjectContainer3D class is the base class for all objects that can serve as DisplayObject3D containers.
	* Each DisplayObjectContainer3D object has its own child list.</span>
	*/
	public class DisplayObjectContainer3D extends EventDispatcher
	{
		/**
		* <span lang="ja">[読み取り専用] シーンは、ツリー構造の DisplayObjectContainer3D のトップです。</span>
		* <span lang="en">[読み取り専用] The scene, which is the top-most DisplayObjectContainer3D in the tree structure.</span>
		*/
		public var root :DisplayObjectContainer3D;
		
		/**
		* <span lang="ja">[internal-use] 子によってインデックスされた名前です。</span>
		* <span lang="en">[internal-use] Names indexed by children.</span>
		* @private
		*/
		protected var _children       :Dictionary;
	
		/**
		* <span lang="ja">[internal-use] 名前によってインデックスされた子です。</span>
		* <span lang="en">[internal-use] Children indexed by name.</span>
		* @private
		*/
		protected var _childrenByName :Object;
	
		private   var _childrenTotal  :int;
		

		/**
		* <span lang="ja">新しい DisplayObjectContainer3D オブジェクトを作成します。</span>
		* <span lang="en">Creates a new DisplayObjectContainer3D object.</span>
		*/
		public function DisplayObjectContainer3D():void
		{
			this._children       = new Dictionary( false );
			this._childrenByName = new Dictionary( true );
			this._childrenTotal  = 0;
		}
	
		/**
		* <span lang="ja">この DisplayObjectContainer インスタンスに、子 DisplayObject3D インスタンスを追加します。</span>
		* <span lang="en">Adds a child DisplayObject3D instance to this DisplayObjectContainer instance.</span>
		*
		* [TODO: If you add a child object that already has a different display object container as a parent, the object is removed from the child list of the other display object container.]
		*
		* @param	child	The DisplayObject3D instance to add as a child of this DisplayObjectContainer3D instance.
		* @param	name	An optional name of the child to add or create. If no name is provided, the child name will be used.
		* @return	The DisplayObject3D instance that you have added or created.
		*/
		public function addChild( child :DisplayObject3D, name:String=null ):DisplayObject3D
		{
			
			if(child.parent)
			{
				PaperLogger.error("DisplayObjectContainer.addChild : DisplayObject3D already has a parent, ie is already added to scene."); 
			}
			// Choose name
			name = name || child.name || String( child.id );
			
			this._children[ child ] = name;
			this._childrenByName[ name ] = child;
			this._childrenTotal++;
	
			child.parent = this;
			child.root = this.root;
	
			return child;
		}
	
	
		/**
		* <span lang="ja">この DisplayObjectContainer インスタンスに、全ての DisplayObject3D インスタンスの子を追加します。</span>
		* <span lang="en">Adds all the children of a DisplayObject3D instance to this DisplayObjectContainer instance.</span>
		*
		* @param	child	The DisplayObjectContainer3D instance that contains the children to add.
		* @return	The DisplayObject3D instance that you have added or created.
		*/
	
		public function addChildren( parent :DisplayObject3D ):DisplayObjectContainer3D
		{
			for each( var child:DisplayObject3D in parent.children )
			{
				parent.removeChild( child );
				this.addChild( child );
			}
	
			return this;
		}
	
	
	
		/**
		* <span lang="ja">DisplayObjectContainer3D インスタンスの子リストから、指定された子 DisplayObject3D インスタンスを削除します。
		* ガーベージコレクターは、使用されないメモリスペースを Flash Player によって再割り当てするプロセスです。
		* 変数またはオブジェクトが、これからどこかで参照または格納されないとき、
		* メモリスペースのためにそれらを削除します。</span>
		* 
		* <span lang="en">Removes the specified child DisplayObject3D instance from the child list of the DisplayObjectContainer3D instance.
		* </p>
		* [TODO: The parent property of the removed child is set to null, and the object is garbage collected if no other references to the child exist.]
		* </p>
		* The garbage collector is the process by which Flash Player reallocates unused memory space. When a variable or object is no longer actively referenced or stored somewhere, the garbage collector sweeps through and wipes out the memory space it used to occupy if no other references to it exist.
		* </p></span>
		* @param	child	The DisplayObject3D instance to remove.
		* @return	The DisplayObject3D instance that you pass in the child parameter.
		*/
	
		/**
		* @public
		* <span lang="ja">Bug #10から追加されました。</span>
		* <span lang="en">Added from Bug #10 by John Grden 8/22/2007</span>
		*/
		public function removeChild( child:DisplayObject3D ):DisplayObject3D
		{
			//removeChildByname(name:string) may return null // must check here
			
			if(child && _children[child]){
				delete _childrenByName[ this._children[ child ] ];
				delete _children[ child ];
			
				child.parent = null;
				child.root = null;
				
				_childrenTotal--;
				
				return child;
			}
			return null;
			
		}
	
		/**
		* <span lang="ja">指定された名前で存在する、子表示オブジェクトを取得します。
		* ひとつの表示オブジェクトが指定された名前をそれ以上持っているとき、このメソッドは子リストの始めのオブジェクトを取得します。</span>
		* 
		* <span lang="en">Returns the child display object that exists with the specified name.
		* If more that one child display object has the specified name, the method returns the first object in the child list.
		* </span>
		* @param	name	The name of the child to return.* 
		* @return	The child display object with the specified name.
		*/
		public function getChildByName( name:String, recursive:Boolean = false ):DisplayObject3D
		{
			if(recursive)
				return findChildByName(name);
			else
				return this._childrenByName[ name ];
		}
	
	
		/**
		* <span lang="ja">指定の名前に存在する DisplayObject3D オブジェクトインスタンスを、DisplayObjectContainer3D インスタンスの子リストから削除します。
		* ひとつの表示オブジェクトが指定された名前をそれ以上持っているとき、このメソッドは子リストの始めのオブジェクトを削除します。
		* ガーベージコレクターは、使用されないメモリスペースを Flash Player によって再割り当てするプロセスです。
		* 変数またはオブジェクトが、これからどこかで参照または格納されないとき、
		* メモリスペースのためにそれらを削除します。</span>
		* 
		* <span lang="en">Removes the child DisplayObject3D instance that exists with the specified name, from the child list of the DisplayObjectContainer3D instance.
		* If more that one child display object has the specified name, the method removes the first object in the child list.
		* [TODO: The parent property of the removed child is set to null, and the object is garbage collected if no other references to the child exist.]
		* The garbage collector is the process by which Flash Player reallocates unused memory space. When a variable or object is no longer actively referenced or stored somewhere, the garbage collector sweeps through and wipes out the memory space it used to occupy if no other references to it exist.
		* </span>
		* 
		* @param	name	The name of the child to remove.
		* @return	The DisplayObject3D instance that was removed.
		*/
		public function removeChildByName( name:String ):DisplayObject3D
		{
			return removeChild( getChildByName( name ) );
		}
	
		/**
		* <span lang="ja">指定されたオブジェクトのストリング表現を返します。</span>
		* <span lang="en">Returns a string value with the list of objects.</span>
		*
		* @return	オブジェクトのストリング表現です。
		*/
		public override function toString():String
		{
			return childrenList();
		}
		
		/**
		* <span lang="ja">オブジェクトのリストで string 値を取得します。</span>
		* <span lang="en">Returns a string value with the list of objects.</span>
		*
		* @return	A string.
		*/
		public function childrenList():String
		{
			var list:String = "";
	
			for( var name:String in this._children )
				list += name + "\n";
	
			return list;
		}
		
		/**
		 * <span lang="ja">それ自体のネームによって、子を再帰的に見つけます。</span>
		 * <span lang="en">Recursively finds a child by its name.</span>
		 * 
		 * @param	name
		 * @param	parent
		 * 
		 * @return 	The found child.
		 */ 
		private function findChildByName(name:String, parent:DisplayObject3D = null):DisplayObject3D
		{
			parent = parent || DisplayObject3D(this);
			if(!parent)
				return null;
			if(parent.name == name)
				return parent;
			for each(var child:DisplayObject3D in parent.children)	
			{
				var obj:DisplayObject3D = findChildByName(name, child);
				if(obj) 
					return obj;
			}
			return null;
		}
		
		/**
		* <span lang="ja">オブジェクトの子の数を取得します。</span>
		* <span lang="en">Returns the number of children of this object.</span>
		*/
		public function get numChildren():int
		{
			return this._childrenTotal;
		}
	
		/**
		* <span lang="ja">子オブジェクトを取得します。</span>
		* <span lang="en">Returns the children object.</span>
		*/
		public function get children():Object
		{
			return this._childrenByName;
		}
	
	}
}
